#include <cassert>
#include <boost/algorithm/string.hpp>
#include <map>

#include "../../../XMLTools.h"
#include "../../../Exceptions.h"

#include "EnvironmentalContactSensor.h"

using namespace MMM;

EnvironmentalContactSensorPtr EnvironmentalContactSensor::loadSensorXML(RapidXMLReaderNodePtr node) {
    EnvironmentalContactSensorPtr sensor(new EnvironmentalContactSensor());
    sensor->loadSensor(node);
    return sensor;
}

EnvironmentalContactSensor::EnvironmentalContactSensor(const std::string &description) : BasicSensor(description)
{
}

bool EnvironmentalContactSensor::checkModel(ModelPtr model) {
    if (!model) {
        MMM_ERROR << "Sensor '" << uniqueName << "' needs a model."  << std::endl;
        return false;
    }
    for (const auto &measurement : measurements) {
        for (const auto &contact : measurement.second->getEnvironmentalContact()) {
            if (!model->getModelNode(contact.first)) return false;
        }
    }
    return true;
}

boost::shared_ptr<BasicSensor<EnvironmentalContactSensorMeasurement> > EnvironmentalContactSensor::cloneConfiguration() {
    EnvironmentalContactSensorPtr m(new EnvironmentalContactSensor(description));
    return m;
}

void EnvironmentalContactSensor::loadConfigurationXML(RapidXMLReaderNodePtr node) {
    assert(node);
    assert(node->name() == "Configuration");
}

void EnvironmentalContactSensor::loadMeasurementXML(RapidXMLReaderNodePtr node, float timestep, SensorMeasurementType type)
{
    assert(node);
    assert(node->name() == XML::NODE_MEASUREMENT);

    std::map<std::string, std::string> EnvironmentalContact;
    if (node->has_node("Contact")) {
        for (auto contactNode : node->nodes("Contact")) {
            std::string name = contactNode->attribute_value("segment");
            if (EnvironmentalContact.find(name) == EnvironmentalContact.end()) EnvironmentalContact[name] = contactNode->value();
            else throw Exception::XMLFormatException("Duplicate of contact at segment '" + name + "' at timestep '" + XML::toString(timestep) + "' in EnvironmentalContact sensor data");
        }
    }
    EnvironmentalContactSensorMeasurementPtr m(new EnvironmentalContactSensorMeasurement(timestep, EnvironmentalContact, type));
    addSensorMeasurement(m);
}

void EnvironmentalContactSensor::appendConfigurationXML(RapidXMLWriterNodePtr node)
{
    assert(node);

    node->append_node("Configuration");
}

bool EnvironmentalContactSensor::hasSensorConfigurationContent() const
{
    return true; // TODO
}

bool EnvironmentalContactSensor::equalsConfiguration(SensorPtr other) {
    EnvironmentalContactSensorPtr ptr = boost::dynamic_pointer_cast<EnvironmentalContactSensor>(other);
    if (ptr) return true;
    else return false;
}

EnvironmentalContactSensorMeasurementPtr EnvironmentalContactSensor::getDerivedMeasurement(float timestep) {
    if (hasMeasurement(timestep)) return measurements[timestep];
    else {
        auto it = measurements.lower_bound(timestep);
        if (it != measurements.end()) {
            EnvironmentalContactSensorMeasurementPtr found = it->second;
            return EnvironmentalContactSensorMeasurementPtr(new EnvironmentalContactSensorMeasurement(found->getTimestep(), found->getEnvironmentalContact(), SensorMeasurementType::EXTENDED));
        }
        else return nullptr;
    }
}

EnvironmentalContactSensorMeasurementPtr EnvironmentalContactSensor::getDerivedMeasurement(float timestep, float delta) {
    return getDerivedMeasurement(timestep);
}

std::string EnvironmentalContactSensor::getType() {
    return TYPE;
}

std::string EnvironmentalContactSensor::getVersion() {
    return VERSION;
}

int EnvironmentalContactSensor::getPriority() {
    return 50;
}
